#ifndef __TOKEN_H_
#define __TOKEN_H_

#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include "base.h"
#include "src.h"

// default:
#define INCLUDE_C_KEY_WORDS
// #define INCLUDE_P4_KEY_WORDS
#define INCLUDE_PY_KEY_WORDS
#define SUPPORT_

typedef enum
{
    TK_BEGIN = 0,
    TK_NONE = 0,
    #define TOKEN(k, s) k,
    #include "token_i.h"
} token_e;

#define TOK_BUF_SIZE 64
#define TOK_BUF_AVAL (TOK_BUF_SIZE - 1)

typedef struct token_t
{
    location_t loc;
    token_e kind = TK_NONE;
    string_ref_t ss;
    char cs[8];
    union {
        int i[2];
        //float f;
        double d;
        void *p = nullptr;
        uint64_t u8;
        int64_t i8;
        const char* s;
        value_t val;
    };
} token_t;

const char* get_token_name(token_e kind);

void print_token(const token_t* tok);

void init_token(const char* buffer, int size, const char* filename/* = NULL */);

void finish_token();

token_t get_token();

token_t foresee_token();

void print_tokens();

int token_error_count();

void make_bak_token_point();

void restore_token_point();

struct option_t;

class token_builder
{
public:
    const char* file_name = nullptr;
    FILE* file = nullptr;
    struct option_t* option = nullptr;
    char c = EOF;
    int line = 1;
    int colume = 0;
    const char* buffer = nullptr;
    int off = 0;
    int size = 0;
    bool forseen = false;
    int forsee = false;
    int error_count = 0;

    bool has_bak = false;
    int bak_line = -1;
    int bak_colume = -1;
    int bak_offset = -1;
    
    void init_token(const char* buffer, int size, const char* filename, struct option_t* option)
    {
        this->file_name = filename;
        this->buffer = buffer;
        this->size = size;
        this->line = 1;
        this->colume = 0;
        this->off = 0;
        this->error_count = 0;
        this->option = option;
        this->c = get_char();
    }

    token_builder() {}

    token_builder(const char* buffer, int size, const char* filename, struct option_t* option)
    {
        init_token(buffer, size, filename, option);
    }

    int get_char()
    {
        if (off >= size) {
            return EOF;
        }
        colume++;
        return buffer[off++];
    }

    int foresee_char()
    {
        if (off >= size) {
            return EOF;
        }
        return buffer[off];
    }
    
    void skip();

    bool expect(token_t* p, char next, token_e kind)
    {
        if (p->kind != kind && c == next) {
            p->kind = kind;
            c = get_char();
            return false;
        } else {
            return true;
        }
    }

    void print_line(const char* format, ...);

    void make_bak_token_point()
    {
        // once only before reset
        assert(has_bak);
        has_bak = true;
        bak_offset = off;
        bak_line = line;
        bak_colume = colume;
    }

    void restore_token_point()
    {
        has_bak = false;
        off = bak_offset;
        line = bak_line;
        colume = bak_colume;
    }

    token_t get_token();

    char parse_trans(token_t* tok);

    void parse_import(token_t* tok);

    void print_tokens();

    int token_error_count()
    {
        return error_count;
    }
};

const char* str(token_t* tok);

char* new_auto_str(token_t* tok);

char* mstrdup(const char* s);

#endif  // __TOKEN_H_